<

インターネットからデータを取得する

インターネットからデータを取得することは、ほとんどのアプリで必要です。 幸いなことに、Dart と Flutter は次のようなツールを提供しています。httpこのタイプの作業用のパッケージ。

このレシピでは次の手順を使用します。

  1. を追加します。httpパッケージ。
  2. を使用してネットワーク リクエストを実行します。httpパッケージ。
  3. 応答をカスタム Dart オブジェクトに変換します。
  4. Flutterでデータを取得して表示します。

1.httpパッケージ

httpパッケージが提供するのは、 インターネットからデータを取得する最も簡単な方法。

追加するには、httpパッケージを依存関係として、 走るflutter pub add:

$ flutter pub add http

http パッケージをインポートします。

import 'package:http/http.dart' as http;

さらに、AndroidManifest.xml ファイルでは、 インターネット許可を追加します。

<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />

2. ネットワークリクエストを行う

このレシピでは、サンプル アルバムをJSONプレースホルダーを使用してhttp.get()方法。

da4ef3a9​​-1434-4ed2-8e26-d356e50b3575

http.get()メソッドはFutureが含まれているResponse

  • Futureを操作するためのコア Dart クラスです。 非同期操作。 Future オブジェクトは可能性を表します 将来のある時点で使用可能になる値またはエラー。
  • http.Responseクラスには、成功したプログラムから受け取ったデータが含まれています httpコール。

3. レスポンスをカスタム Dart オブジェクトに変換します。

ネットワークリクエストを行うのは簡単ですが、生のデータを扱うのは簡単です。Future<http.Response>あまり便利ではありません。 あなたの生活を楽にするために、 を変換しますhttp.ResponseDart オブジェクトに変換します。

を作成しますAlbumクラス

まず、Albumからのデータを含むクラス ネットワークリクエスト。これにはファクトリ コンストラクターが含まれています。 を作成しますAlbumJSONから。

JSON を手動で変換することは 1 つのオプションにすぎません。 詳細については、記事全文を参照してください。JSONとシリアル化

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({
    required this.userId,
    required this.id,
    required this.title,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}

変換するhttp.ResponseAlbum

次に、次の手順を使用して、fetchAlbum()を返す関数Future<Album>:

  1. レスポンスボディをJSONに変換するMapと のdart:convertパッケージ。
  2. サーバーがステータス コードを含む OK 応答を返した場合、 200、次に JSON を変換しますMapAlbumを使用してfromJson()ファクトリーメソッド。
  3. サーバーがステータス コード 200 の OK 応答を返さない場合、 その後、例外をスローします。 (サーバー応答が「404 Not Found」の場合でも、 例外をスローします。戻らないでくださいnull。 調べるときに大事なこと のデータsnapshot、以下に示すように。)
Future<Album> fetchAlbum() async {
  final response = await http
      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body));
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

万歳! これで、インターネットからアルバムを取得する機能ができました。

4. データを取得する

電話してくださいfetchAlbum()どちらかの方法でinitState()またdidChangeDependencies()方法。

initState()メソッドは 1 回だけ呼び出され、その後は呼び出されません。 応答として API をリロードするオプションが必要な場合は、InheritedWidget変更して、電話をdidChangeDependencies()方法。 見るState詳細については。

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }
  // ···
}

このFutureは次のステップで使用されます。

5. データを表示する

データを画面に表示するには、FutureBuilderウィジェット。 のFutureBuilderウィジェットには Flutter が付属しており、 非同期データ ソースの操作が簡単になります。

次の 2 つのパラメータを指定する必要があります。

  1. Future一緒に働きたいと思っています。 この場合、未来は次から返されます。 のfetchAlbum()関数。
  2. builderFlutterに伝える関数 に応じて何をレンダリングするか の状態Future: 読み込み、成功、またはエラー。

ご了承くださいsnapshot.hasDataのみを返しますtrueスナップショットに null 以外のデータ値が含まれている場合。

なぜならfetchAlbumnull 以外の値のみを返すことができます。 関数は例外をスローする必要があります 「404 Not Found」サーバー応答の場合でも同様です。 例外をスローすると、snapshot.hasErrortrueエラーメッセージを表示するために使用できます。

それ以外の場合は、スピナーが表示されます。

FutureBuilder<Album>(
  future: futureAlbum,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Text(snapshot.data!.title);
    } else if (snapshot.hasError) {
      return Text('${snapshot.error}');
    }

    // By default, show a loading spinner.
    return const CircularProgressIndicator();
  },
)

fetchAlbum() が initState() で呼び出されるのはなぜですか?

便利ではあるものの、 API 呼び出しをbuild()方法。

Flutter はbuild()必要なときはいつでもメソッド ビュー内の何かを変更するには、 そしてこれは驚くほど頻繁に起こります。 のfetchAlbum()メソッド内に配置された場合build()、繰り返します 再構築のたびに呼び出されるため、アプリの速度が低下します。

保管するfetchAlbum()状態変数の結果は次のことを保証します のFutureは 1 回だけ実行され、その後はキャッシュされます。 再構築します。

テスト

この機能をテストする方法については、 次のレシピを参照してください。

完全な例

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<Album> fetchAlbum() async {
  final response = await http
      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body));
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({
    required this.userId,
    required this.id,
    required this.title,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Album>(
            future: futureAlbum,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!.title);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }

              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}